package geometry;

import java.util.ArrayList;
import java.util.List;

import javax.media.j3d.Node;
import constraint.Variable;
import constraint.VariableType;
import foundationgeom.Axis;
import foundationgeom.EulerAngles;
import foundationgeom.Transformation;
import foundationgeom.Vector;
import graph.AbstractNode;
import graph.GNode;
import graph.NodeType;

/**
 * A combo box is a collection of boxes. The coordinate system of the combo box is aligned with
 * the coordinate system of the first box in the list of boxes. 
 * @author jay
 *
 */
public class ComboBox extends AbstractNode implements Geometry, GNode {
	private List<Box> boxes = new ArrayList<Box>();
	private CoordSys cs;
	public String name="";
	
	public String toString() {
	    return boxes.toString();
	}
		
	public ComboBox(List<Box>boxes, String name) {
	    super(NodeType.PART, "");
        this.boxes = boxes;
        cs = new CoordSys(World.getCS(), this);
        for (Box box : boxes) {
            box.getCoordSys().changeParentTo(this.cs);
        }
	    this.name = name;
	}

    public Transformation getTransformation(Geometry geometry) {
	    return this.cs.getTransformationTo(geometry.getCoordSys()); 
	}
	
	public Vector getCSDifferece(ComboBox box) {
	    Vector difference = this.cs.difference(box.cs);
	    return difference;
	}
	
	public void translateBy(double dx, double dy, double dz) {
		cs.translate(Vector.create(dx,dy,dz));
	}
	
	public void print() {
		System.out.println(toString());
	}
	
	public String toVRML() {
	    String s = "";
	    for (Box box : boxes) {
	        s.concat(box.toVRML());
	    }
		return s;
	}

    public List<Node> getTransormedJava3DBox(float red, float blue, float green, float scale) {        
        List<Node> nodes = new ArrayList<Node>();
        for (Box box : boxes) {
            box = box.copy(cs);
            box.changeParent(World.getCS());
            nodes.addAll(box.getTransormedJava3DBox(red, blue, green, scale));
        }
        return nodes;
    }

    public Vector getScale(Geometry geometry) {
        if (geometry instanceof ComboBox) {
            ComboBox comboBox = (ComboBox) geometry;
            if (comboBox.boxes.size() != this.boxes.size()) {
                return null; 
            }
            
            Vector scale = comboBox.boxes.get(0).getScale(this.boxes.get(0));
            for (int i = 1; i<this.boxes.size();i++) {
                boolean matchFound = false;
                for (int j =1; j<comboBox.boxes.size(); j++) {
                    Vector scale2 = comboBox.boxes.get(i).getScale(this.boxes.get(j));
                    if (!scale2.equals(scale)) {
                        continue;
                    }
                    Vector o1 = comboBox.boxes.get(i).getCoordSys().getOriginDisplacement();
                    Vector o2 = this.boxes.get(j).getCoordSys().getOriginDisplacement();
                    Vector quo = Vector.create(o1.getX()/o2.getX(),o1.getY()/o2.getY(),o1.getZ()/o2.getZ());
                    if (quo.equals(scale)) {
                        matchFound = true;
                    }
                }
                if(!matchFound) {
                    return null;
                }
            }
            return scale; 
        }
        System.out.println("TRYING TO GENERATE SCALE BETWEEN TWO OBJECTS THAT ARE NOT THE SAME TYPE.");
        return null;
    }
    
    public Geometry transform(CoordSys parent, Transform transformation) {
        ComboBox combo = this.copy(parent);
        Vector o = combo.cs.getOriginDisplacement();
        Vector t = transformation.transformation.displacement();
        Vector sum = o.add(t);
        combo.cs.setOriginDisplacement(sum);
        EulerAngles angles = combo.cs.getEulerAngles();
        EulerAngles transformedAngles = transformation.transformation.apply(angles);
        combo.cs.setOrientation(transformedAngles);
        for(Box box : combo.boxes) {
            Vector originisplacement = box.getCoordSys().getOriginDisplacement();
            box.getCoordSys().setOriginDisplacement(Vector.create(
                    originisplacement.getX()*transformation.sx, 
                    originisplacement.getY()*transformation.sy, 
                    originisplacement.getZ()*transformation.sz));
            box.scale(transformation.sx, transformation.sy, transformation.sz);
        }
        return combo;
    }

    public boolean areEqual(Geometry geometry) {
        if (geometry instanceof ComboBox == false) {
            return false;
        }
        ComboBox combo = (ComboBox) geometry;
        for (Box box : this.boxes) {
            boolean isEqual = false;
            for (Box box1 : combo.boxes) {
                if (box.areEqual(box1)) {
                    isEqual = true;
                    break;
                }
            }
            if (!isEqual) {
                return false;
            }
        }
        return this.cs.equal(combo.cs);
    }
    
    public void scale(double sx, double sy, double sz) {
        for (Box box : this.boxes) {
            box.scale(sx, sy, sz);
            Vector disp = box.getCoordSys().getOriginDisplacement();
            Vector disp2 = Vector.create(
                    disp.getX()*sx, 
                    disp.getY()*sy,
                    disp.getZ()*sz);
            box.getCoordSys().setOriginDisplacement(disp2);
        }
    }

    public void rotate(double x, double y, double z) {
        this.cs.rotate(Axis.X, x);
        this.cs.rotate(Axis.Y, y);
        this.cs.rotate(Axis.Z, z);
    }

    public ComboBox copy(CoordSys parent) {
        ComboBox combo = new ComboBox(new ArrayList<Box>(), name);
        combo.cs.changeParentTo(parent);
        combo.cs.setOriginDisplacement(this.cs.getOriginDisplacement());
        combo.cs.setOrientation(this.cs.getAxialFrame());
        for (Box box : this.boxes) {
            combo.boxes.add(box.copy(combo.cs));
        }
        return combo;
    }

    public boolean intersects(Geometry geometry) {
        //TODO this only works for intersections in the x-y plane for boxes that are rotated 0, 90, 180, or 270 degrees
//        if (geometry instanceof ComboBox) {
//            ComboBox box = (ComboBox) geometry;
//            Transformation t1 = this.cs.getTransformationTo(World.getCS());
//            Transformation t2 = box.cs.getTransformationTo(World.getCS()); 
//            
//            Point b1 = Point.create(0,0);
//            Point b2 = Point.create(box.xdim.getDoubleValue(),0);
//            Point b3 = Point.create(0,box.ydim.getDoubleValue());
//            Point b4 = Point.create(box.xdim.getDoubleValue(), box.ydim.getDoubleValue());
//            b1 = t2.apply(b1);
//            b2 = t2.apply(b2);
//            b3 = t2.apply(b3);
//            b4 = t2.apply(b4);
//            
//            Point p1 = Point.create(0,0);
//            Point p2 = Point.create(xdim.getDoubleValue(),0);
//            Point p3 = Point.create(0,ydim.getDoubleValue());
//            Point p4 = Point.create(xdim.getDoubleValue(), ydim.getDoubleValue());
//            p1 = t1.apply(p1);
//            p2 = t1.apply(p2);
//            p3 = t1.apply(p3);
//            p4 = t1.apply(p4);
//
//            double maxXB = Math.max(b1.getX(), Math.max(b2.getX(), Math.max(b3.getX(), b4.getX())));
//            double maxYB = Math.max(b1.getY(), Math.max(b2.getY(), Math.max(b3.getY(), b4.getY())));
//            double minXB = Math.min(b1.getX(), Math.min(b2.getX(), Math.min(b3.getX(), b4.getX())));
//            double minYB = Math.min(b1.getY(), Math.min(b2.getY(), Math.min(b3.getY(), b4.getY())));
//            
//            double maxXP = Math.max(p1.getX(), Math.max(p2.getX(), Math.max(p3.getX(), p4.getX())));
//            double maxYP = Math.max(p1.getY(), Math.max(p2.getY(), Math.max(p3.getY(), p4.getY())));
//            double minXP = Math.min(p1.getX(), Math.min(p2.getX(), Math.min(p3.getX(), p4.getX())));
//            double minYP = Math.min(p1.getY(), Math.min(p2.getY(), Math.min(p3.getY(), p4.getY())));
//            
//            if (minXB > maxXP || minXP > maxXB) { //they are not overlapping in x --> no intersection
//                return false;
//            }
//            if (minYB > maxYP || minYP > maxYB) { //they are not overlapping in x --> no intersection
//                return false;
//            }
//            
//            return true;
//        } else {
//            System.out.println("CAN'T DO INTERSECTION CALS FOR THIS TYPE");
//        }
        return false;
    }

    public CoordSys getCoordSys() {
        return this.cs;
    }

    public Variable getVariable(VariableType type) {
        // TODO Auto-generated method stub
        return null;
    }

    public Variable[] getVariables() {
        // TODO Auto-generated method stub
        return null;
    }

    public GNode copy() {
        return this.copy(cs.getParent());
    }

    public GNode transformInSiblingCS(Transform transformation,
            Geometry lhsGeom, Geometry designGeom) {
        ComboBox combo = this.copy(designGeom.getCoordSys());
        combo.cs.changeParentTo(World.getCS());
        return combo;    }
}
